#region Copyright Notice
// 
// 
//  
//  Copyright (c) 2009-2011 Anindya Chatterjee
//  
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//  
//      http://www.apache.org/licenses/LICENSE-2.0
//  
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// 
// 
#endregion
using System;
using System.Text.RegularExpressions;
using AbstractClass.Extensions;

namespace AbstractClass
{
    /// <summary>
    /// Represents a binary equivalent of 32-bit signed integer.
    /// </summary>
    public struct BinaryNumber : IComparable<BinaryNumber>, IEquatable<BinaryNumber>, IComparable, IFormattable,
                                 IConvertible
    {
        private readonly int _binaryNumber;
        private readonly Regex _binaryPattern;
        private readonly Regex _binaryPatternB;

        /// <summary>
        /// Initializes a new instance of the <see cref="BinaryNumber"/>.
        /// </summary>
        /// <param name="binary">The binary number string.</param>
        /// <exception cref="BinaryNumberFormatException">Input string contains character other than 0 or 1.</exception>
        public BinaryNumber(string binary)
        {
            _binaryPattern = new Regex("^[0-1]+$");
            _binaryPatternB = new Regex("^0b[0-1]+$");
            if (!_binaryPattern.IsMatch(binary) && !_binaryPatternB.IsMatch(binary))
                throw new BinaryNumberFormatException();
            binary = binary.Remove("0b");
            _binaryNumber = Convert.ToInt32(binary, 2);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BinaryNumber"/>.
        /// </summary>
        /// <param name="number">A 32-bit integer.</param>
        public BinaryNumber(int number)
        {
            _binaryPattern = new Regex("^[0-1]+$");
            _binaryPatternB = new Regex("^0b[0-1]+$");
            _binaryNumber = number;
        }

        #region IComparable Members

        /// <summary>
        /// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.
        /// </summary>
        /// <param name="obj">An object to compare with this instance.</param>
        /// <returns>
        /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has these meanings:
        /// Value
        /// Meaning
        /// Less than zero
        /// This instance is less than <paramref name="obj"/>.
        /// Zero
        /// This instance is equal to <paramref name="obj"/>.
        /// Greater than zero
        /// This instance is greater than <paramref name="obj"/>.
        /// </returns>
        /// <exception cref="T:System.ArgumentException">
        /// 	<paramref name="obj"/> is not the same type as this instance.
        /// </exception>
        public int CompareTo(object obj)
        {
            var binaryNumber = (BinaryNumber) obj;
            return _binaryNumber.CompareTo(binaryNumber._binaryNumber);
        }

        #endregion

        #region IConvertible Members

        /// <summary>
        /// Returns the <see cref="T:System.TypeCode"/> for this instance.
        /// </summary>
        /// <returns>
        /// The enumerated constant that is the <see cref="T:System.TypeCode"/> of the class or value type that implements this interface.
        /// </returns>
        public TypeCode GetTypeCode()
        {
            return TypeCode.Int32;
        }

        bool IConvertible.ToBoolean(IFormatProvider provider)
        {
            return Convert.ToBoolean(_binaryNumber, provider);
        }

        char IConvertible.ToChar(IFormatProvider provider)
        {
            return Convert.ToChar(_binaryNumber, provider);
        }

        sbyte IConvertible.ToSByte(IFormatProvider provider)
        {
            return Convert.ToSByte(_binaryNumber, provider);
        }

        byte IConvertible.ToByte(IFormatProvider provider)
        {
            return Convert.ToByte(_binaryNumber, provider);
        }

        short IConvertible.ToInt16(IFormatProvider provider)
        {
            return Convert.ToInt16(_binaryNumber, provider);
        }

        ushort IConvertible.ToUInt16(IFormatProvider provider)
        {
            return Convert.ToUInt16(_binaryNumber, provider);
        }

        int IConvertible.ToInt32(IFormatProvider provider)
        {
            return Convert.ToInt32(_binaryNumber, provider);
        }

        uint IConvertible.ToUInt32(IFormatProvider provider)
        {
            return Convert.ToUInt32(_binaryNumber, provider);
        }

        long IConvertible.ToInt64(IFormatProvider provider)
        {
            return Convert.ToInt64(_binaryNumber, provider);
        }

        ulong IConvertible.ToUInt64(IFormatProvider provider)
        {
            return Convert.ToUInt64(_binaryNumber, provider);
        }

        float IConvertible.ToSingle(IFormatProvider provider)
        {
            return Convert.ToSingle(_binaryNumber, provider);
        }

        double IConvertible.ToDouble(IFormatProvider provider)
        {
            return Convert.ToDouble(_binaryNumber, provider);
        }

        decimal IConvertible.ToDecimal(IFormatProvider provider)
        {
            return Convert.ToDecimal(_binaryNumber, provider);
        }

        DateTime IConvertible.ToDateTime(IFormatProvider provider)
        {
            return Convert.ToDateTime(_binaryNumber, provider);
        }

        string IConvertible.ToString(IFormatProvider provider)
        {
            return Convert.ToString(_binaryNumber, provider);
        }

        object IConvertible.ToType(Type conversionType, IFormatProvider provider)
        {
// ReSharper disable AssignNullToNotNullAttribute
            return Convert.ChangeType(_binaryNumber, conversionType, provider);
// ReSharper restore AssignNullToNotNullAttribute
        }

        #endregion

        #region IEquatable<BinaryNumber> Members

        /// <summary>
        /// Determines whether a specified binary number is equal to this instance.
        /// </summary>
        /// <param name="other">The other binary number.</param>
        /// <returns><c>true</c> if specified binary number is equal to this instance; otherwise, <c>false</c>.</returns>
        public bool Equals(BinaryNumber other)
        {
            return this == other;
        }

        #endregion

        #region IFormattable Members

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <param name="format">The format.</param>
        /// <param name="formatProvider">The format provider.</param>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public string ToString(string format, IFormatProvider formatProvider)
        {
            return _binaryNumber.ToString(format, formatProvider);
        }

        #endregion

        /// <summary>
        /// Returns the base 10 equivalent of this binary number.
        /// </summary>
        /// <returns>A 32-bit integer.</returns>
        public int ToInt32()
        {
            return _binaryNumber;
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return Convert.ToString(_binaryNumber, 2);
        }

        /// <summary>
        /// Calculates the 2's complement of <paramref name="binaryNumber"/>.
        /// </summary>
        /// <param name = "binaryNumber">A binary number.</param>
        /// <returns>A <see cref="BinaryNumber"/> equals to the 2's complement of the <paramref name="binaryNumber"/>.</returns>
        public static BinaryNumber operator !(BinaryNumber binaryNumber)
        {
            return new BinaryNumber(-1 * binaryNumber._binaryNumber);
        }

        /// <summary>
        /// Returns the equivalent base-10 32-bit integer.
        /// </summary>
        /// <param name="binaryNumber">A binary number.</param>
        /// <returns>A base 10 equivalent of <paramref name="binaryNumber"/>.</returns>
        public static implicit operator int(BinaryNumber binaryNumber)
        {
            return binaryNumber._binaryNumber;
        }

        /// <summary>
        /// Returns the equivalent binary number of a base-10 32-bit integer.
        /// </summary>
        /// <param name="number"></param>
        /// <returns></returns>
        public static implicit operator BinaryNumber(int number)
        {
            return new BinaryNumber(number);
        }

        /// <summary>
        /// Returns a hash code for this instance.
        /// </summary>
        /// <returns>
        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
        /// </returns>
        public override int GetHashCode()
        {
            return _binaryNumber;
        }

        /// <summary>
        /// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
        /// </summary>
        /// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
        /// <returns>
        /// 	<c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
        /// </returns>
        public override bool Equals(object obj)
        {
            var other = (BinaryNumber) obj;
            return _binaryNumber == other._binaryNumber;
        }

        #region Implementation of IComparable<BinaryNumber>

        /// <summary>
        /// Compares this instance to a specified <see cref="BinaryNumber"/> and returns an indication of their relative values.
        /// </summary>
        /// <param name="other">The other binary number to compare with this instance.</param>
        /// <returns>A signed number indicating the relative values of this instance and value, as shown in the following table
        /// <list type="">
        /// <item>Less than zero : current instance is less than other</item>
        /// <item>Zero : current instance is equal to other</item>
        /// <item>Greater than zero : current instance is greater than other</item>
        /// </list>
        /// </returns>
        public int CompareTo(BinaryNumber other)
        {
            return _binaryNumber.CompareTo(other._binaryNumber);
        }

        #endregion
    }
}